package org.carabiner.infopanel;

import java.awt.AWTEvent;
import java.awt.Component;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import org.carabiner.util.CarabinerException;

/**
 * Uses a set of rules to generate a set of warnings about a
 * {@link java.awt.Component}.
 * 
 * @author Ben Rady (benrady@gmail.com)
 * @see org.carabiner.util.CarabinerProperties#getInspectorListFilename()
 */
public class Inspector
{
  private Set ruleSet;
  private Component selectedComp;
  private Component testSubject;
  private DefaultTreeModel treeModel;

  /**
   * Creates an Inspector using a list of Rules defined in the file.
   * 
   * @param inspectorListFile
   *          File
   * @see org.carabiner.util.CarabinerProperties#getInspectorListFilename()
   */
  public Inspector(File inspectorListFile, Component testComponent)
  {    
    testSubject = testComponent;
    ruleSet = new HashSet();
    try
    {
      BufferedReader reader = new BufferedReader(new FileReader(
          inspectorListFile));
      String classname = null;
      do
      {
        classname = reader.readLine();
        if (classname != null && !classname.startsWith(";")
            && !classname.startsWith("!"))
        {
          try
          {
            Class clazz = Class.forName(classname);
            Object newRule = clazz.newInstance();
            if (newRule instanceof Rule)
            {
              addRule((Rule) newRule);
            } else
            {
              throw new CarabinerException(
                  "Class "
                      + classname
                      + " defined in "
                      + inspectorListFile
                      + " does not implement org.carabiner.infopanel.inspector.Rule");
            }
          } catch (ClassNotFoundException ex)
          {
            throw new CarabinerException("Cannot find class " + classname
                + " defined in inspector list file: " + inspectorListFile, ex);
          } catch (IllegalAccessException ex)
          {
            throw new CarabinerException("Cannot access class " + classname
                + " defined in inspector list file: " + inspectorListFile, ex);
          } catch (InstantiationException ex)
          {
            throw new CarabinerException("Cannot create class " + classname
                + " defined in inspector list file: " + inspectorListFile, ex);
          }
        }
      } while (classname != null);
      reader.close();
    } catch (IOException ex)
    {
      throw new CarabinerException("Error reading inspector list file: "
          + inspectorListFile, ex);
    }
  }

  /**
   * Creates an inspector and adds a default set of rules
   */
  public Inspector(Component testComponent)
  {
    testSubject = testComponent;
    
    // Use default rule set
    ruleSet = new HashSet();
    addRule(new ZeroSizeRule());
    addRule(new UnconnectedControlRule());
    addRule(new NoTitleRule());
    addRule(new EmptyJLabelRule());
  }

  public void addRule(Rule rule)
  {
    ruleSet.add(rule);
  }

  /**
   * Inspects a component for usability errors. The results are returned as a
   * map of {@link java.awt.Component components} (keys) and
   * {@link org.carabiner.infopanel.Warning warnings} (values).
   * 
   * @param component
   *          Component
   * @return Map
   */
  public List inspect()
  {
    List warnings = new ArrayList();
    for (Iterator i = ruleSet.iterator(); i.hasNext();)
    {
      Rule rule = (Rule) i.next();
      warnings.addAll(rule.inspect(testSubject));
    }
    return warnings;
  }

  private CarabinerComponentNode findNode(Component child)
  {
//    CarabinerComponentNode root = (CarabinerComponentNode) treeModel.getRoot();    
    return null;
  }
  
  /**
   * For testing purposes only
   * 
   * @return Object
   */
  int getRuleCount()
  {
    return ruleSet.size();
  }

  /**
   * Returns the component selected in the inspector panel, or null if none selected
   * 
   * @return
   */
  public Component getSelectedComponent()
  {
    return selectedComp;
  }
  
  void setSelectedComponent(Component component)
  {
    selectedComp = component;
  }

  void addEvent(AWTEvent event)
  {
    CarabinerComponentNode node = findNode((Component)event.getSource()); 
    if (node != null)
    {
      //node.addEvent(event);
    }    
  }

  public Component getComponent()
  {
    return testSubject;
  }

  public TreeModel getTreeModel()
  {
    if (treeModel == null)
    {
      treeModel = new CarabinerMessageTreeModel(testSubject, inspect());
    }    
    return treeModel;
  }

  public TreePath getPathToRoot(CarabinerComponentNode node)
  {
    return new TreePath(treeModel.getPathToRoot(node));
  }
}
